package com.authine.cloudpivot.ext.applicationservice;

import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.alibaba.cola.exception.Exceptions;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.authine.cloudpivot.ext.constants.AppConst;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.constants.PlatformConst;
import com.authine.cloudpivot.ext.constants.ServiceUrlConst;
import com.authine.cloudpivot.ext.dto.VendorJoinDto;
import com.authine.cloudpivot.ext.dto.system.*;
import com.authine.cloudpivot.ext.enums.system.ResourceTypeEnum;
import com.authine.cloudpivot.ext.enums.system.RowTypeEnum;
import com.authine.cloudpivot.ext.utils.BoServiceUtils;
import com.authine.cloudpivot.ext.utils.HttpClients;
import com.authine.cloudpivot.ext.utils.JdbcTemplateUtils;
import com.authine.cloudpivot.ext.utils.TypeReferenceUtils;
import com.authine.hermes.app.launcher.service.ApplicationService;
import com.authine.mvp.app.launcher.domain.SelectorFormat;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.authine.mvp.app.launcher.dto.bo.response.BO;
import com.authine.mvp.app.launcher.dto.errorcode.ErrorCodeEnum;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.authine.hermes.app.launcher.service.ApplicationServiceFoundation.CUSTOM_SERVICE;

/**
 * @ClassName VendorRequestService
 * @Description: 供应商加盟
 * @Author fj
 * @Date 2021/6/29
 * @Version V1.0
 **/
@Path("VendorJoinService")
@Slf4j
public class VendorJoinService extends ApplicationService {
    private static final NamedParameterJdbcTemplate jdbcTemplate = ApplicationContextUtils.getBean(NamedParameterJdbcTemplate.class);

    /**
     * 更新供应商加入状态
     *
     * @param vendorJoinDto 供应商加盟信息
     * @return              响应结果
     */
    @Path("updateJoinStatus")
    @POST
    @Operation(summary = "更新供应商加入状态", tags = CUSTOM_SERVICE)
    public SingleResponse<BO> updateJoinStatus(@Parameter VendorJoinDto vendorJoinDto) {
        Map<String, Object> vendorJoinMap = Maps.newHashMap();
        vendorJoinMap.put("id", vendorJoinDto.getId());
        vendorJoinMap.put("join_status", vendorJoinDto.getJoin_status());
        SingleResponse<BO> vendorJoinResult = BoServiceUtils.updateMainBo(ModelConst.T_VENDOR_JOIN, vendorJoinMap);
        log.info("------------------------更新供应商加盟数据成功，更新后结果：{}------------------------", JSONObject.toJSONString(vendorJoinResult));
        return vendorJoinResult;
    }

    /**
     * 审批同意：供应商加盟，客户入驻
     *
     * @param vendorJoinDto     供应商加盟信息
     * @return                  响应结果
     */
    @Path("approve")
    @POST
    @Operation(summary = "审批同意：供应商加盟，客户入驻", tags = CUSTOM_SERVICE)
    public SingleResponse<BO> approve(@Parameter VendorJoinDto vendorJoinDto) {
        Assert.isTrue("1".equals(vendorJoinDto.getJoin_status()), "操作失败！必须是已提交状态才能审批！");

        checkUser(vendorJoinDto);

        // 保存账户信息
        SingleResponse<BO> accountBO = saveAccount(vendorJoinDto);
        if (!accountBO.isSuccess()) {
            Exceptions.throwBizException(ErrorCodeEnum.BO_SAVE_ERROR);
        }
        log.info("------------------------保存账户成功，账户信息：{}------------------------", JSONObject.toJSONString(accountBO));

        // 保存组织
        SingleResponse<BO> organizationBO = saveOrganization(vendorJoinDto, accountBO.getData().getBizObjectId());
        if (!organizationBO.isSuccess()) {
            Exceptions.throwBizException(new BizException(organizationBO.getErrMessage()));
        }

        // 保存部门
        SingleResponse<BO> departmentBO = saveDepartment(vendorJoinDto, organizationBO.getData().getData());
        if (!departmentBO.isSuccess()) {
            Exceptions.throwBizException(new BizException(departmentBO.getErrMessage()));
        }

        // 保存角色
        SingleResponse<BO> roleBO = saveRole(vendorJoinDto, organizationBO.getData().getData());
        if (!roleBO.isSuccess()) {
            Exceptions.throwBizException(new BizException(roleBO.getErrMessage()));
        }

        // 保存用户
        saveUser(vendorJoinDto, roleBO.getData().getBizObjectId(), organizationBO.getData().getData(), departmentBO.getData().getData());

        // 如果有供应商邀请码，修改供应商邀请码为可用
        if ("1".equals(vendorJoinDto.getArchitecture_type())) {
            if (!StringUtils.isEmpty(vendorJoinDto.getInvition_code())) {
                List<Map<String, Object>> checkCode = getInviteCodeList(vendorJoinDto.getInvition_code().trim());
                log.info("------------------------通过组织邀请码查询数据返回结果：{}------------------------", JSONObject.toJSONString(checkCode));
                if (checkCode.size() > 0) {
                    Map<String, Object> organizationInviteCodeMap = Maps.newHashMap();
                    organizationInviteCodeMap.put("id", checkCode.get(0).get("id"));
                    organizationInviteCodeMap.put("organization_id", organizationBO.getData().getData().get("id"));
                    organizationInviteCodeMap.put("organization_name", organizationBO.getData().getData().get("name"));
                    SingleResponse<BO> inviteCodeUpdateBO = BoServiceUtils.updateMainBo(ModelConst.T_ORGANIZATION_INVITE_CODE, organizationInviteCodeMap);
                    log.info("------------------------组织邀请码更新成功，更新后结果：{}------------------------", JSONObject.toJSONString(inviteCodeUpdateBO));
                }
            }
        }
        Response vendorJoinDeleteBO = BoServiceUtils.deleteBo(ModelConst.T_VENDOR_JOIN, vendorJoinDto.getId(), true);
        log.info("------------------------供应商/客户数据从加盟表中删除成功：{}------------------------", JSONObject.toJSONString(vendorJoinDeleteBO));
        return SingleResponse.of(new BO());
    }

    /**
     * 校验用户信息
     *
     * @param vendorJoinDto 供应商加盟信息
     */
    private void checkUser(VendorJoinDto vendorJoinDto) {
        Map<String, Object> param = Maps.newHashMap();
        // 通过用户名查询
        param.put("username", vendorJoinDto.getUsername());
        String url = HttpClients.getUrl(AppConst.SYSTEM_MANAGEMENT, ServiceUrlConst.USER_COUNT_URL);
        Integer count = HttpClients.execute(url, param, Integer.class);
        Assert.isTrue(count == 0, "用户名已存在");
        param.clear();

        // 通过邮箱查询
        param.put("email", vendorJoinDto.getEmail());
        count = HttpClients.execute(url, param, Integer.class);
        Assert.isTrue(count == 0, "邮箱已存在");
        param.clear();

        // 通过手机号查询
        param.put("cellphone", vendorJoinDto.getMobile());
        count = HttpClients.execute(url, param, Integer.class);
        Assert.isTrue(count == 0, "手机号已存在");
        param.clear();

        if (!StringUtils.isEmpty(vendorJoinDto.getInvition_code())) {
            // 查询邀请码是否存在
            List<Map<String, Object>> checkCode = getInviteCodeList(vendorJoinDto.getInvition_code().trim());
            log.info("------------------------通过组织邀请码查询数据返回结果：{}------------------------", JSONObject.toJSONString(checkCode));
            Assert.isTrue(checkCode.size() > 0, "邀请码无效");

            // 判断邀请码是否被使用
            StringBuilder sql = new StringBuilder();
            Map<String, Object> sqlParam = Maps.newHashMap();
            sql.append("select * from\n");
            sql.append(JdbcTemplateUtils.getTableName(ModelConst.T_ORGANIZATION_INVITE_CODE, AppConst.PERSONAL_CENTER)).append(" \n");
            sql.append("where vendor_organization_id is not null\n");
            sql.append("and code = :code");
            sqlParam.put("code", vendorJoinDto.getInvition_code().trim());
            log.info("sql = {}, parameters = {}", sql, JSONObject.toJSONString(sqlParam));
            List<Map<String, Object>> result = jdbcTemplate.queryForList(sql.toString(), sqlParam);
            log.info("------------------------通过组织邀请码和供应商查询数据返回结果：{}------------------------", JSONObject.toJSONString(result));
            Assert.isTrue(CollectionUtils.isEmpty(result), "邀请码已使用");
        }
    }

    /**
     * 保存账户信息
     *
     * @param vendorJoinDto 供应商信息
     * @return              响应结果
     */
    public SingleResponse<BO> saveAccount(VendorJoinDto vendorJoinDto) {
        Map<String, Object> accountMap = Maps.newHashMap();
        accountMap.put("account_balance", 0);
        accountMap.put("credit_limit", BigDecimal.ZERO);
        accountMap.put("available_limit", BigDecimal.ZERO);
        accountMap.put("bonus", BigDecimal.ZERO);
        accountMap.put("platform_rate", "2".equals(vendorJoinDto.getArchitecture_type()) ? PlatformConst.PLATRATE : BigDecimal.ZERO);
        accountMap.put("service_rate", BigDecimal.ZERO);
        accountMap.put("other_rate", BigDecimal.ZERO);
        return BoServiceUtils.createBo(ModelConst.T_ACCOUNT, Collections.singletonList(accountMap));
    }

    /**
     * 保存组织信息
     *
     * @param vendorJoinDto     供应商信息
     * @param accountId         账户id
     * @return                  响应结果
     */
    private SingleResponse<BO> saveOrganization(VendorJoinDto vendorJoinDto, String accountId) {
        OrganizationDto organization = OrganizationDto.builder()
                .name(vendorJoinDto.getCompany_name())
                .account_id(accountId)
                .organization_product_line(vendorJoinDto.getMajor_product_category())
                .business_scope(vendorJoinDto.getMajor_project())
                .license_code(vendorJoinDto.getLicense_code())
                .credit_identifier(vendorJoinDto.getLicense_code())
                .legal_representative(vendorJoinDto.getLegal_representative())
                .registered_capital(vendorJoinDto.getRegistered_capital().toPlainString())
                .description(vendorJoinDto.getMajor_project())
                .memo(vendorJoinDto.getMemo())
                .cellphone(vendorJoinDto.getMobile())
                .email(vendorJoinDto.getEmail())
                .connect(vendorJoinDto.getContact())
                .attachment(vendorJoinDto.getLicense())
                .freight_ratio(null)
                .statement_cycle("2".equals(vendorJoinDto.getArchitecture_type()) ? Integer.parseInt(vendorJoinDto.getAccount_period()) : 0)
                .enabled(false)
                .platform(false)
                .small_scale_rate(vendorJoinDto.getPlatform_fee() != null ? new BigDecimal(vendorJoinDto.getPlatform_fee()) : null)
                // 创建角色时回写
//                .role_names("2".equals(vendorJoinDto.getArchitecture_type()) ? "ROLE_CUSTOMER_ADMIN" : "ROLE_VENDOR_ADMIN")
                .small_scale(vendorJoinDto.getSmall() != null ? vendorJoinDto.getSmall() : false)
                .build();
        String url = HttpClients.getUrl(AppConst.SYSTEM_MANAGEMENT, ServiceUrlConst.SAVE_ORG_URL);
        Map<String, Object> param = JSON.parseObject(JSON.toJSONString(organization));
        String result = HttpClients.execute(url, param);
        return TypeReferenceUtils.readValueToSingleResponse(result);
    }

    /**
     * 保存部门信息
     *
     * @param vendorJoinDto         供应商信息
     * @param organizationMap       组织信息
     * @return                      响应结果
     */
    private SingleResponse<BO> saveDepartment(VendorJoinDto vendorJoinDto, Map<String, Object> organizationMap) {
        // 设置部门组织
        SelectorFormat organization = new SelectorFormat();
        String organizationName = String.valueOf(organizationMap.get("name"));
        organization.setType(1);
        organization.setId(String.valueOf(organizationMap.get("id")));
        organization.setName(organizationName);
        DepartmentDto department = DepartmentDto.builder()
                .organization_id(Collections.singletonList(organization))
                .number("1001")
                .name(vendorJoinDto.getCompany_name().concat("默认部门"))
                .description(vendorJoinDto.getCompany_name().concat("默认部门"))
                .build();
        String url = HttpClients.getUrl(AppConst.SYSTEM_MANAGEMENT, ServiceUrlConst.SAVE_DEPT_URL);
        Map<String, Object> param = JSON.parseObject(JSON.toJSONString(department));
        String result =  HttpClients.execute(url, param);
        return TypeReferenceUtils.readValueToSingleResponse(result);
    }

    /**
     * 保存角色信息
     * @param vendorJoinDto     供应商信息
     * @param organizationMap   组织信息
     * @return                  响应结果
     */
    private SingleResponse<BO> saveRole(VendorJoinDto vendorJoinDto, Map<String, Object> organizationMap) {
        // 设置组织
        SelectorFormat organization = new SelectorFormat();
        String organizationName = String.valueOf(organizationMap.get("name"));
        organization.setType(1);
        organization.setId(String.valueOf(organizationMap.get("id")));
        organization.setName(organizationName);

        // 获取默认权限
        List<ResourceDto> resourceList = Objects.requireNonNull(JdbcTemplateUtils.builder(AppConst.SYSTEM_MANAGEMENT, ModelConst.T_RESOURCE)
                .selectField("id, parent_id, name, type, level, sort, appCode, permId, menuCode, groupCode, menuItemCode, componentCode, pageCode"))
                .eq("enabled", "1")
                .eq("appCode", AppConst.SOURCING_CENTER)
                .queryForJavaBean(ResourceDto.class);

        // 获取寻源中心应用
        List<ResourceDto> appResource = resourceList.stream()
                .filter(resourceDto -> ResourceTypeEnum.APP.getCode().equals(resourceDto.getType()))
                .collect(Collectors.toList());

        // 默认给我的询价权限
        ResourceDto myInquiry = resourceList.stream()
                .filter(resourceDto -> "我的询价".equals(resourceDto.getName()))
                .findFirst()
                .orElseThrow(() -> new BizException("资源不存在，请初始化数据"));

        // 获取我的询价页面资源
        List<ResourceDto> myInquiryChildren = resourceList.stream()
                .filter(resourceDto -> myInquiry.getId().equals(resourceDto.getParent_id()))
                .collect(Collectors.toList());


        appResource.add(myInquiry);
        appResource.addAll(myInquiryChildren);
        log.info("------------------寻源中心资源列表数据：{}---------------------", JSONObject.toJSONString(resourceList));
        RoleDto roleDto = RoleDto.builder()
                .organization_id(Collections.singletonList(organization))
                .organization_name(organizationName)
                .name("2".equals(vendorJoinDto.getArchitecture_type()) ? PlatformConst.ROLE_CUSTOMER_ADMIN : PlatformConst.ROLE_VENDOR_ADMIN)
                .description(vendorJoinDto.getCompany_name().concat("默认部门管理角色"))
                .resourceList(appResource)
                // todo 暂时设为全部可见，等产品组织权限完善后设置为组织可见
                .row_type(RowTypeEnum.PM_MANAGER.getCode())
                // 默认按人员授权
                .authType("2")
                .build();
        String url = HttpClients.getUrl(AppConst.SYSTEM_MANAGEMENT, ServiceUrlConst.SAVE_ROLE_URL);
        Map<String, Object> param = JSON.parseObject(JSON.toJSONString(roleDto));
        String result =  HttpClients.execute(url, param);
        return TypeReferenceUtils.readValueToSingleResponse(result);
    }

    /**
     * 保存用户信息
     *
     * @param vendorJoinDto         供应商信息
     * @param roleId                角色id
     * @param organizationMap       组织信息
     * @param departmentMap         部门信息
     */
    private void saveUser(VendorJoinDto vendorJoinDto, String roleId, Map<String, Object> organizationMap, Map<String, Object> departmentMap) {
        // 设置组织
        SelectorFormat organization = new SelectorFormat();
        String organizationName = String.valueOf(organizationMap.get("name"));
        organization.setType(1);
        organization.setId(String.valueOf(organizationMap.get("id")));
        organization.setName(organizationName);

        // 设置部门
        SelectorFormat department = new SelectorFormat();
        String departmentName = String.valueOf(departmentMap.get("name"));
        department.setType(1);
        department.setId(String.valueOf(departmentMap.get("id")));
        department.setName(departmentName);

        UserDto user = UserDto.builder()
                .organization_id(Collections.singletonList(organization))
                .department_id(Collections.singletonList(department))
                .username(vendorJoinDto.getUsername())
                .cellphone(vendorJoinDto.getMobile())
                .email(vendorJoinDto.getEmail())
                .fullname(vendorJoinDto.getCompany_name())
                .password(vendorJoinDto.getPassword())
                .role_ids(roleId)
                .enabled(false)
                .expired(false)
                .locked(false)
                .credentials_expired(false)
                .protocol_account(false)
                .index(vendorJoinDto.getIndex())
                .encryptedNewPassword(vendorJoinDto.getEncryptedNewPassword())
                .encryptedOldPassword(vendorJoinDto.getEncryptedOldPassword())
                .build();
        String url = HttpClients.getUrl(AppConst.SYSTEM_MANAGEMENT, ServiceUrlConst.SAVE_USER_URL);
        Map<String, Object> param = JSON.parseObject(JSON.toJSONString(user));
        String result =  HttpClients.execute(url, param);
        SingleResponse<BO> response = TypeReferenceUtils.readValueToSingleResponse(result);
        if (!response.isSuccess()) {
            Exceptions.throwBizException(new BizException(response.getErrMessage()));
        }
    }

    /**
     * 获取邀请码列表
     *
     * @param code  组织邀请码
     * @return  查询结果
     */
    private List<Map<String, Object>> getInviteCodeList(String code) {
        List<Map<String, Object>> result = Objects.requireNonNull(JdbcTemplateUtils.builder(AppConst.PERSONAL_CENTER, ModelConst.T_ORGANIZATION_INVITE_CODE)
                .selectField("*"))
                .eq("code", code)
                .queryForList();
        if (CollectionUtils.isEmpty(result)) {
            return Lists.newArrayList();
        }
        return result;
    }
}
